#include <windows.h>
#include "CChar.h"
#include "CParty.h"
#include "CAlliance.h"
#include "CUpdatePartyPacket.h"
#include "CDefinePartyPacket.h"
#include "CMessagePacketTwo.h"
#include "define.h"

CParty::CParty(void) {
	this->alliance = NULL; //Alliance pointer
	this->pLeader = NULL; //Party leader
	this->lvlSync = NULL; //LevelSync target
	this->qm = NULL; //Quarter Master
	this->inAlliance = 0; //We aren't in an alliance
	partyListSemaphore=new CSemaphore(1);
	unlockPartyList();
}

CParty::~CParty() {
	lockPartyList();
	delete partyListSemaphore;
}

bool CParty::lockPartyList(void)
{
	return partyListSemaphore->lock();
}

bool CParty::unlockPartyList(void)
{
	return partyListSemaphore->unlock();
}

//Starts the party disbanding process after verifiying all possible reasons of an invalid  disband
int CParty::startDisband(CChar * member) {
	//If the player is a party leader
	if (this->getLeader() == member) {
		//Check to see if the leader was in an alliance
		if ((this->inAlliance > 0) && (this->getAlliance() != NULL) ) {
			//Check to see if they were the leader of the alliance
			if (this->getAlliance()->getMainParty()->getLeader()->getId() == member->getId() ) {
				//Disband the entire alliance.
				this->getAlliance()->dissolveAlliance();
			} else {
				//Remove the party
				this->getAlliance()->removeParty(this);
			}
		} else {
			//Single party, disbanding

			//Send the drop for everyone
			member->insertPacket(CHAR_INPARTY_SELF,CDefinePartyPacket(NULL));
			//Drop everyone from the party
			this->disbandParty();
		}
	}
	return 0;
}

int CParty::assignPartyRole(CChar * assigner,CChar * assignee,int role) {
	//Check to make sure the person is in the same party
	if (assignee->getParty() == assigner->getParty()) {
		//make sure they are the party leader.
		if (this->getLeader() == assigner) {
			switch(role) {
				case 0x00: //Change Leader
					this->setLeader(assignee);
					break;
				case 0x04: //Set QM
					this->setQM(assignee);
					break;
				case 0x05: //Remove QM
					this->setQM(NULL);
					break;
				case 0x06: //Level Sync
					//getDisplayZone - char is in Moghouse, get prevzone.  Char in Moghouse can assign a party role to someone outside of Moghouse.
					if (assigner->getDisplayZone() != assignee->getDisplayZone()) {
						//Error message, can't level sync if not in same zone
						assigner->insertPacket(CHAR_INRANGE_SELF, CMessagePacketTwo(assigner, assigner, 0, 0x6A046A04, 0x1E02));
					} else {
						this->setLvlSync(assignee);
					}
			}
			//Send a packet to everyone in the party to update any changes
			assigner->insertPacket(CHAR_INALLIANCE_SELF,CDefinePartyPacket(this));
			assignee->updatePartyStats();
			//assigner->insertPacket(CHAR_INALLIANCE_SELF,CUpdatePartyPacket(
		}
	}
	return 0;
}

//Kicks everyone out of the party
int CParty::disbandParty(void) {
	//Un-set the party leader
	this->setLeader(NULL);
	//Remove the QM
	this->setQM(NULL);
	//Remove the Level sync
	this->setLvlSync(NULL);
	//Lock the party list
	this->lockPartyList();
	//kick everyone out of the party and remove the party
	for (int i=0;i<(int)members.size();i++) {
		//update everyone in the party to show they are getting kicked
		this->reloadParty(members[i],true);
		//Kick from the party
		members[i]->setParty(NULL);
		//Drops your treasure pool
		members[i]->getTreasurePool()->removeMember(members[i]);
	}
	//Clear the party members list
	members.clear();
	//Unlock the party list
	this->unlockPartyList();
	return 0;
}

//Returns the number of members in the party
unsigned int CParty::memberCount(void) {	
	if (!members.empty()) return (unsigned int) members.size();
	return 0;
}

int CParty::removeMember(CChar * member) {
	//Saftey check to prevent a potential deadlock
	int listLocked = 0;
	
	if ( (this != NULL) && (member != NULL) ) {
		//Verify that the leader is not being kicked.
		if ( member != this->getLeader() ) {
			//Send the drop define for the player
			member->quePacket(CDefinePartyPacket(NULL));
			//Lock the party members list to remove the member
			this->lockPartyList();
			listLocked = 1;
			//Remove everyone in the party for the person whos kicked
			this->reloadParty(member,true);
			for (unsigned int x = 0; x < this->members.size(); x++) {
				//Check to see if the member is the one we are removing
				if (member == this->members.at(x)) {
					//Match found, shift down the inParty numbers 
					for (unsigned int y = x; y < this->members.size(); y++) {
						//Is this member a valid pointer?
						if (this->members.at(y) != NULL) {
							//Valid member, do a quick refresh of the members party index
							this->members.at(y)->inParty--;
						}
					}
					//Erase the character out of the members list
					this->members.erase(this->members.begin()+x,this->members.begin()+x+1);

					//We removed the member, we can safely unlock the list now
					this->unlockPartyList();
					listLocked = 0;
					
					//If the person getting kicked is QM, disable QM
					if (this->getQM() == member) {
						this->setQM(NULL);
					}

					//If the person getting kicked is the lvlsync, disable it
					if (this->getLvlSync() == member) {
						this->setLvlSync(NULL);
					}

					//Update everyone in the party of the change
					this->getLeader()->insertPacket(CHAR_INALLIANCE_SELF,CDefinePartyPacket(this));
					//It doesn't really matter the perspective of whos getting the update since they are being removed
					this->getLeader()->insertPacket(CHAR_INALLIANCE_SELF,CUpdatePartyPacket(this->getLeader(),member,0,0));
					// *BOOT*
					member->setParty(NULL);
					//Reload everyone remaining
					this->getLeader()->updatePartyStats();
					//Drop the leaver from the treasure pool
					member->getTreasurePool()->removeMember(member);
					break;
				}
			}
		} else if ((this->inAlliance > 0) && (this->getAlliance() != NULL)) {
			if (this->getAlliance()->getMainParty() != this) {
				//Leaving the alliance
				this->getAlliance()->removeParty(this);
			} else {
				//alliance leader is leaving
				member->forcePartyLeave();
			}
		} else {
			//Force leave, pass leader to the next member (same as disconnect)
			member->forcePartyLeave();
		}
	}

	//if this is equal to one...somehow we tried to remove someone in this party who is not in this party...
	if (listLocked != 0)
		this->unlockPartyList();
		
	return 0;
}

int CParty::addMember(CChar * member) {
	//Drop the old treasure pool
	delete member->getTreasurePool();
	//Lock the party list to add the member
	this->lockPartyList();
	//Add the new member to the party list
	members.push_back(member);
	//unlock the list
	this->unlockPartyList();
	//Assign new members party index number
	member->inParty = (unsigned int) members.size();
	//Tell the new member they are in a party
	member->setParty(this);
	//If your the first member, you do not need your treasure pool reset
	if (member->inParty != 0) {
		//Clear your old pool
		member->clearTreasurePool();
		//Set your new pool to null so we can find a new one
		member->setTreasurePool(NULL);
		//Join the parties pool
		member->getTreasurePool();
	}
	return 0;
}

//Returns a pointer to the party leader
CChar * CParty::getLeader(void) {
	return this->pLeader;
}

//Returns a pointer to the party lvlsync target
CChar * CParty::getLvlSync(void) {
	return this->lvlSync;
}

//Returns a pointer to the quarter master
CChar * CParty::getQM(void) {
	return this->qm;
}

//Returns a pointer to the alliance this party belongs to
CAlliance * CParty::getAlliance(void) {
	return this->alliance;
}

//Re-sends all of the players in your party / alliance hp/mp/tp values FOR THE schar PLAYER ONLY
int CParty::reloadParty(CChar * schar,bool bRemove) {
	//Check to see if the party is part of an alliance
	if ( (this->inAlliance > 0) && (this->getAlliance() != NULL) ) {
		//If bRemove is true we will ALWAYS have locked it previously, so don't touch it
		if (bRemove == false) 
			this->getAlliance()->lockAllianceList();
		//The party is part of an alliance, now check all of the parties in the alliance for members
		for(unsigned int x = 0; x < this->getAlliance()->partyList.size(); x++) {
			//Get the first party from the alliance
			CParty * subParty = this->getAlliance()->partyList.at(x);
			//Verify the pointer of the party is not null.
			if (subParty != NULL) {
				//If bRemove is true we will ALWAYS have locked it previously, so don't touch it
				if (bRemove == false) 
					subParty->lockPartyList();
				//Check every member inside of the alliance party
				for (unsigned int y = 0; y < subParty->members.size(); y++) {
					CChar * partyChar = subParty->members.at(y);
					if (partyChar != NULL) {
						if (bRemove == false) {
							//Generate an update for everyone in the alliance
							//schar - yourself, partyChar - the person currently being generated
							schar->quePacket(CUpdatePartyPacket(schar,partyChar,subParty->inAlliance,partyChar->inParty + (subParty->inAlliance * 6)));
						} else { //Removing the party member
							schar->quePacket(CUpdatePartyPacket(schar,partyChar,0,0));
						}
					}
				}
				//If bRemove is true we will ALWAYS have locked it previously, so don't touch it
				if (bRemove == false) 
					subParty->unlockPartyList();
			}
		}

		//Unlock the alliance party list
		//If bRemove is true we will ALWAYS have locked it previously, so don't touch it
		if (bRemove == false) 
			this->getAlliance()->unlockAllianceList();

		//Not in an alliance
	} else {

		//Check every member inside of the alliance party
		//If bRemove is true we will ALWAYS have locked it previously, so don't touch it
		if (bRemove == false) 
			this->lockPartyList();
		for (unsigned int y = 0; y < this->members.size(); y++) {
			CChar * partyChar = this->members.at(y);
			if (partyChar != NULL) {
				if (bRemove == false) {
					//Generate an update for everyone in the party
					//schar - yourself, partyChar - the person currently being generated, alliance index #, member index # in the party
					schar->quePacket(CUpdatePartyPacket(schar,partyChar,this->inAlliance,partyChar->inParty));
				} else { //Removing the party member 
					schar->quePacket(CUpdatePartyPacket(schar,partyChar,0,0));
				}
			}
		}
		//If bRemove is true we will ALWAYS have locked it previously, so don't touch it
		if (bRemove == false) 
			this->unlockPartyList();
	}
	return 0;
}


//Assigns a party leader for the party
int CParty::setLeader(CChar * leader) {
	this->pLeader = leader;
	return 0;
}

//Assigns the level sync target for the party (copies their level)
int CParty::setLvlSync(CChar * lvlSync) {
	this->lvlSync = lvlSync;
	return 0;
}

//Assigns the Quarter Master for the party
int CParty::setQM(CChar * qm) {
	this->qm = qm;
	return 0;
}

//Assigns the party to an alliance, or removes the party from an alliance
int CParty::setAlliance(CAlliance * alliance) {
	if (alliance == NULL) {
		//If we are setting the value to null, chances are we are no longer in an alliance
		this->inAlliance = 0;
	}
	//Regardless of the result above; we will set the alliance
	this->alliance = alliance;
	return 0;
}

//Destroys the treasure pool for the party
void CParty::dropTreasurePool(void) {
	//Kicks everyone out of the treasure pool
	for (int i=0;i<(int)members.size();i++) {
		if (members[i] != NULL) {
			//If you have a treasure pool, it will be dropped
			if (members[i]->hasTreasurePool()) {
				//Drops your treasure pool
				members[i]->getTreasurePool()->removeMember(members[i]);
			}
		}
	}	
}

//re-assigns the treasure pool for the party
void CParty::rebuildTreasurePool(void) {
	//Gets the treasure pool for each person in the party
	for (int i=0;i<(int)members.size();i++) {
		if (members[i] != NULL) {
			//Check to see if you have a treasure pool
			if (members[i]->hasTreasurePool() == 0) {
				//Since you do not have a treasure pool, get one now
				members[i]->getTreasurePool();
			}
		}
	}	
}